<?php

/**
 * MDB2 Aborior's Simplex driver
 *
 * This driver is a read-only driver, as Aborior's Simplex is now defunct
 *
 * PHP versions 5
 *
 * LICENSE:
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. The name of the authors may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @category  Database
 * @package   MDB2_Driver_simplex
 * @author    Philippe Jausions <jausions@php.net>
 * @copyright 2008 11abacus / Philippe Jausions
 * @license   http://www.opensource.org/licenses/bsd-license.php BSD License
 * @version   CVS: $Id: $
 * @link      http://pear.11abacus.com/package/MDB2_Driver_simplex
 */

/**
 * Required DB_Simplex class
 */
require_once 'DB/Simplex.php';

/**
 * MDB2 Aborior's Simplex driver
 * 
 * This driver is a read-only driver, as Aborior's Simplex is now defunct
 *
 * @category  Database
 * @package   MDB2_Driver_simplex
 * @author    Philippe Jausions <jausions@php.net>
 * @copyright 2008 11abacus /  Philippe Jausions
 * @license   http://www.opensource.org/licenses/bsd-license.php BSD License
 * @link      http://pear.11abacus.com/package/MDB2_Driver_simplex
 */
class MDB2_Driver_simplex extends MDB2_Driver_Common
{
    /**
     * String quoting parameters
     *
     * @var    array
     * @access protected
     */
    var $string_quoting = array(
        'start' => "'", 
        'end' => "'", 
        'escape' => "'", 
        'escape_pattern' => false,
    );

    /**
     * Identifier quoting parameters
     *
     * @var    array
     * @access protected
     */
    var $identifier_quoting = array(
        'start' => '"', 
        'end' => '"', 
        'escape' => '"',
    );

    protected $_lasterror = '';

    /**
     * Constructor
     */
    public function __construct()
    {
        parent::__construct();

        $this->phptype = 'simplex';
        $this->dbsyntax = 'simplex';

        $this->supported['sequences'] = 'emulated';
        $this->supported['primary_key'] = false; // requires alter table implementation
        $this->supported['result_introspection'] = false; // not implemented
        $this->supported['prepared_statements'] = 'emulated';
        $this->supported['identifier_quoting'] = false;
        $this->supported['pattern_escaping'] = false;
        $this->supported['new_link'] = false;

        $this->options['DBA_username'] = false;
        $this->options['DBA_password'] = false;
        $this->options['fixed_float'] = 0;
        $this->options['database_path'] = '';
        $this->options['server_version'] = '';
        $this->options['max_identifiers_length'] = 128; //no real limit
    }

    /**
     * Returns information about an error
     *
     * @param integer $error Error message to lookup 
     * 
     * @return array
     */
    public function errorInfo($error = null)
    {
        static $error_regexps = array(
            '/^no such table:/' => MDB2_ERROR_NOSUCHTABLE,
            '/^no such index:/' => MDB2_ERROR_NOT_FOUND,
            '/^(table|index) .* already exists$/' => MDB2_ERROR_ALREADY_EXISTS,
            '/PRIMARY KEY must be unique/i' => MDB2_ERROR_CONSTRAINT,
            '/is not unique/' => MDB2_ERROR_CONSTRAINT,
            '/columns .* are not unique/i' => MDB2_ERROR_CONSTRAINT,
            '/uniqueness constraint failed/' => MDB2_ERROR_CONSTRAINT,
            '/may not be NULL/' => MDB2_ERROR_CONSTRAINT_NOT_NULL,
            '/^no such column:/' => MDB2_ERROR_NOSUCHFIELD,
            '/no column named/' => MDB2_ERROR_NOSUCHFIELD,
            '/column not present in both tables/i' => MDB2_ERROR_NOSUCHFIELD,
            '/^near ".*": syntax error$/' => MDB2_ERROR_SYNTAX,
            '/[0-9]+ values for [0-9]+ columns/i' => MDB2_ERROR_VALUE_COUNT_ON_ROW,
        );

        $native_code = null;

        if ($error === null) {
            foreach ($error_regexps as $regexp => $code) {
                if (preg_match($regexp, $native_msg)) {
                    $error = $code;
                    break;
                }
            }
        }
        return array($error, $native_code, $native_msg);
    }

    /**
     * Connects to the "server" / database file
     *
     * @return boolena TRUE on success, MDB2 Error Object on failure
     */
    public function connect()
    {        
        if (is_object($this->connection)) {
            if (MDB2::areEquals($this->connected_dsn, $this->dsn)
                && $this->connected_database_name == $this->database_name
                && $this->opened_persistent == $this->options['persistent']
            ) {
                return MDB2_OK;
            }
            $this->disconnect(false);
        }

        if (!empty($this->database_name)) {
            try {
                $connection = new DB_Simplex($this->dsn['hostspec'],
                                             $this->database_name);
            } catch (Exception $e) {
                return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
                'unable to establish a connection', __FUNCTION__);
            }

            $this->connection    = $connection;
            $this->connected_dsn = $this->dsn;
            $this->connected_database_name = $this->database_name;
            $this->dbsyntax = $this->dsn['dbsyntax'] 
                              ? $this->dsn['dbsyntax'] : $this->phptype;
        }
        return MDB2_OK;
    }

    /**
     * Checks if given database name exists?
     *
     * @param string $name name of the database that should be checked
     *
     * @return boolean TRUE/FALSE on success, a MDB2 error on failure
     */
    public function databaseExists($name)
    {
        return $this->connection->databaseExists($name);
    }

    /**
     * Logs out and disconnects from the "server"
     *
     * @param boolean $force Whether to force disconnect
     * 
     * @return boolean TRUE on success, FALSE if not connected and error
     *                object on error
     */
    public function disconnect($force = true)
    {
        if (is_object($this->connection)) {
            $this->connection->disconnect();
        } elseif (!$force) {
            return false;
        }
        return parent::disconnect($force);
    }

    /**
     * Executes a query
     * 
     * @param string     $query  query
     * @param boolean    $is_manip  if the query is a manipulation query
     * @param DB_Simplex $connection
     * @param string     $database_name
     * 
     * @return result or error object
     * @access protected
     */
    function _doQuery($query, $is_manip = false, $connection = null, 
                       $database_name = null)
    {
        $this->last_query = $query;
        $result = $this->debug($query, 'query', array('is_manip' => $is_manip,
                                                      'when' => 'pre'));
        if ($result) {
            if (PEAR::isError($result)) {
                return $result;
            }
            $query = $result;
        }
        if ($this->options['disable_query']) {
            $result = $is_manip ? 0 : null;
            return $result;
        }

        if (is_null($connection)) {
            $connection = $this->getConnection();
            if (PEAR::isError($connection)) {
                return $connection;
            }
        }

        if ($is_manip) {
            $err =& $this->raiseError(null, null, null,
                'Manip queries are not supported', __FUNCTION__);
            return $err;
        }
        
        echo __FILE__.' ['.__LINE__."] \n";
        echo $query;
        exit;
        
        if (!$result) {
            $err =& $this->raiseError(null, null, null,
                'Could not execute statement', __FUNCTION__);
            return $err;
        }

        $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'post', 'result' => $result));
        return $result;
    }

    /**
     * Returns the number of rows affected (Not implemented yet)
     *
     * @param resource $result
     * @param resource $connection
     * 
     * @return integer the number of rows affected or MDB2 Error Object
     * @access protected
     */
    function _affectedRows($connection, $result = null)
    {
        if (is_null($connection)) {
            $connection = $this->getConnection();
            if (PEAR::isError($connection)) {
                return $connection;
            }
        }
        return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
            'not implemented', __FUNCTION__);
    }

    /**
     * Changes a query string for various DBMS specific reasons
     *
     * @param string  $query    query to modify
     * @param boolean $is_manip if it is a DML query
     * @param integer $limit    limit the number of rows
     * @param integer $offset   start reading from given offset
     * 
     * @return string modified query
     * @access protected
     */
    function _modifyQuery($query, $is_manip, $limit, $offset)
    {
        if ($this->options['portability'] & MDB2_PORTABILITY_DELETE_COUNT) {
            if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
                $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
                                      'DELETE FROM \1 WHERE 1=1', $query);
            }
        }
        if ($limit > 0
            && !preg_match('/LIMIT\s*\d(?:\s*(?:,|OFFSET)\s*\d+)?(?:[^\)]*)?$/i', $query)
        ) {
            $query = rtrim($query);
            if (substr($query, -1) == ';') {
                $query = substr($query, 0, -1);
            }
            if ($is_manip) {
                $query.= " LIMIT $limit";
            } else {
                $query.= " LIMIT $offset,$limit";
            }
        }
        return $query;
    }

    /**
     * Returns version information about the server
     *
     * @param bool $native Whether the raw version string should be returned
     * 
     * @return mixed array/string with version information or MDB2 error object
     */
    public function getServerVersion($native = false)
    {
        $server_info = '@package_version@';
        if (!$native) {
            $tmp = explode('.', $server_info, 3);
            $server_info = array(
                'major' => isset($tmp[0]) ? $tmp[0] : null,
                'minor' => isset($tmp[1]) ? $tmp[1] : null,
                'patch' => isset($tmp[2]) ? $tmp[2] : null,
                'extra' => null,
                'native' => $server_info,
            );
        }
        return $server_info;
    }
}

/**
 * MDB2 Aborior's Simplex result driver
 *
 * @category Database
 * @package  MDB2_Driver_simplex
 * @author   Philippe Jausions <jausions@php.net>
 */
class MDB2_Result_simplex extends MDB2_Result_Common
{
    /**
     * Fetch a row and insert the data into an existing array.
     *
     * @param int       $fetchmode  how the array data should be indexed
     * @param int    $rownum    number of the row where the data can be found
     * 
     * @return int data array on success, a MDB2 error on failure
     */
    public function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
    {
        if (!is_null($rownum)) {
            $seek = $this->seek($rownum);
            if (PEAR::isError($seek)) {
                return $seek;
            }
        }
        if ($fetchmode == MDB2_FETCHMODE_DEFAULT) {
            $fetchmode = $this->db->fetchmode;
        }
        if ($fetchmode & MDB2_FETCHMODE_ASSOC) {
            $row = @sqlite_fetch_array($this->result, SQLITE_ASSOC);
            if (is_array($row)
                && $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
            ) {
                $row = array_change_key_case($row, $this->db->options['field_case']);
            }
        } else {
           $row = @sqlite_fetch_array($this->result, SQLITE_NUM);
        }
        if (!$row) {
            if ($this->result === false) {
                $err =& $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
                    'resultset has already been freed', __FUNCTION__);
                return $err;
            }
            $null = null;
            return $null;
        }
        $mode = $this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL;
        $rtrim = false;
        if ($this->db->options['portability'] & MDB2_PORTABILITY_RTRIM) {
            if (empty($this->types)) {
                $mode += MDB2_PORTABILITY_RTRIM;
            } else {
                $rtrim = true;
            }
        }
        if ($mode) {
            $this->db->_fixResultArrayValues($row, $mode);
        }
        if (!empty($this->types)) {
            $row = $this->db->datatype->convertResultRow($this->types, $row, $rtrim);
        }
        if (!empty($this->values)) {
            $this->_assignBindColumns($row);
        }
        if ($fetchmode === MDB2_FETCHMODE_OBJECT) {
            $object_class = $this->db->options['fetch_class'];
            if ($object_class == 'stdClass') {
                $row = (object) $row;
            } else {
                $row = &new $object_class($row);
            }
        }
        ++$this->rownum;
        return $row;
    }

    /**
     * Retrieves the column names returned by the DBMS in a query result.
     *
     * @return array List holding the names of columns as keys
     *                  or an MDB2 error on failure.
     * @access protected
     */
    function _getColumnNames()
    {
        $columns = array_flip($this->result->getColumnNames());
        if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
            $columns = array_change_key_case($columns, $this->db->options['field_case']);
        }
        return $columns;
    }

    /**
     * Counts the number of columns returned by the DBMS in a query result.
     *
     * @return integer Number of columns, a MDB2 error on failure
     */
    public function numCols()
    {
        $cols = $this->result->countCols();
        if ($cols === null) {
            if ($this->result === false) {
                return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
                    'resultset has already been freed', __FUNCTION__);
            } elseif ($this->result === null) {
                return count($this->types);
            }
            return $this->db->raiseError(null, null, null,
                'Could not get column count', __FUNCTION__);
        }
        return $cols;
    }
}

/**
 * MDB2 Aborior's Simplex buffered result driver
 *
 * @category Database
 * @package  MDB2_Driver_simplex
 * @author   Philippe Jausions <jausions@php.net>
 */
class MDB2_BufferedResult_simplex extends MDB2_Result_simplex
{
    /**
     * Seek to a specific row in a result set
     *
     * @param int    $rownum    number of the row where the data can be found
     * @return mixed MDB2_OK on success, a MDB2 error on failure
     */
    public function seek($rownum = 0)
    {
        if (!@sqlite_seek($this->result, $rownum)) {
            if ($this->result === false) {
                return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
                    'resultset has already been freed', __FUNCTION__);
            } elseif (is_null($this->result)) {
                return MDB2_OK;
            }
            return $this->db->raiseError(MDB2_ERROR_INVALID, null, null,
                'tried to seek to an invalid row number ('.$rownum.')', __FUNCTION__);
        }
        $this->rownum = $rownum - 1;
        return MDB2_OK;
    }

    /**
     * Check if the end of the result set has been reached
     *
     * @return mixed true or false on sucess, a MDB2 error on failure
     */
    public function valid()
    {
        $numrows = $this->numRows();
        if (PEAR::isError($numrows)) {
            return $numrows;
        }
        return $this->rownum < ($numrows - 1);
    }

    /**
     * Returns the number of rows in a result object
     *
     * @return mixed MDB2 Error Object or the number of rows
     */
    public function numRows()
    {
        $rows = $this->result->count();
        if (is_null($rows)) {
            if ($this->result === false) {
                return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
                    'resultset has already been freed', __FUNCTION__);
            } elseif (is_null($this->result)) {
                return 0;
            }
            return $this->db->raiseError(null, null, null,
                'Could not get row count', __FUNCTION__);
        }
        return $rows;
    }
}

/**
 * MDB2 Aborior's Simplex statement driver
 *
 * @category Database
 * @package  MDB2_Driver_smiplex
 * @author   Philippe Jausions <jausions@php.net>
 */
class MDB2_Statement_simplex extends MDB2_Statement_Common
{

}

?>